// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Lowers the AST to the HIR.
//
// Since the AST and HIR are fairly similar, this is mostly a simple procedure,
// much like a fold. Where lowering involves a bit more work things get more
// interesting and there are some invariants you should know about. These mostly
// concern spans and ids.
//
// Spans are assigned to AST nodes during parsing and then are modified during
// expansion to indicate the origin of a node and the process it went through
// being expanded. Ids are assigned to AST nodes just before lowering.
//
// For the simpler lowering steps, ids and spans should be preserved. Unlike
// expansion we do not preserve the process of lowering in the spans, so spans
// should not be modified here. When creating a new node (as opposed to
// 'folding' an existing one), then you create a new id using `next_id()`.
//
// You must ensure that ids are unique. That means that you should only use the
// id from an AST node in a single HIR node (you can assume that AST node ids
// are unique). Every new node must have a unique id. Avoid cloning HIR nodes.
// If you do, you must then set the new node's id to a fresh one.
//
// Spans are used for error messages and for tools to map semantics back to
// source code. It is therefore not as important with spans as ids to be strict
// about use (you can't break the compiler by screwing up a span). Obviously, a
// HIR node can only have a single span. But multiple nodes can have the same
// span and spans don't need to be kept in order, etc. Where code is preserved
// by lowering, it should have the same span as in the AST. Where HIR nodes are
// new it is probably best to give a span for the whole AST node being lowered.
// All nodes should have real spans, don't use dummy spans. Tools are likely to
// get confused if the spans from leaf AST nodes occur in multiple places
// in the HIR, especially for multiple identifiers.

use hir;
use hir::map::{Definitions, DefKey};
use hir::map::definitions::DefPathData;
use hir::def_id::{DefIndex, DefId};
use hir::def::{Def, PathResolution};
use session::Session;
use util::nodemap::{DefIdMap, NodeMap};

use std::collections::BTreeMap;
use std::iter;
use std::mem;

use syntax::attr;
use syntax::ast::*;
use syntax::errors;
use syntax::ptr::P;
use syntax::codemap::{self, respan, Spanned};
use syntax::std_inject;
use syntax::symbol::{Symbol, keywords};
use syntax::util::small_vector::SmallVector;
use syntax::visit::{self, Visitor};
use syntax_pos::Span;

pub struct LoweringContext<'a> {
    crate_root: Option<&'static str>,
    // Use to assign ids to hir nodes that do not directly correspond to an ast node
    sess: &'a Session,
    // As we walk the AST we must keep track of the current 'parent' def id (in
    // the form of a DefIndex) so that if we create a new node which introduces
    // a definition, then we can properly create the def id.
    parent_def: Option<DefIndex>,
    resolver: &'a mut Resolver,

    /// The items being lowered are collected here.
    items: BTreeMap<NodeId, hir::Item>,

    trait_items: BTreeMap<hir::TraitItemId, hir::TraitItem>,
    impl_items: BTreeMap<hir::ImplItemId, hir::ImplItem>,
    bodies: BTreeMap<hir::BodyId, hir::Body>,

    trait_impls: BTreeMap<DefId, Vec<NodeId>>,
    trait_default_impl: BTreeMap<DefId, NodeId>,

    loop_scopes: Vec<NodeId>,
    is_in_loop_condition: bool,

    type_def_lifetime_params: DefIdMap<usize>,
}

pub trait Resolver {
    // Resolve a hir path generated by the lowerer when expanding `for`, `if let`, etc.
    fn resolve_hir_path(&mut self, path: &mut hir::Path, is_value: bool);

    // Obtain the resolution for a node id
    fn get_resolution(&mut self, id: NodeId) -> Option<PathResolution>;

    // We must keep the set of definitions up to date as we add nodes that weren't in the AST.
    // This should only return `None` during testing.
    fn definitions(&mut self) -> &mut Definitions;
}

pub fn lower_crate(sess: &Session,
                   krate: &Crate,
                   resolver: &mut Resolver)
                   -> hir::Crate {
    // We're constructing the HIR here; we don't care what we will
    // read, since we haven't even constructed the *input* to
    // incr. comp. yet.
    let _ignore = sess.dep_graph.in_ignore();

    LoweringContext {
        crate_root: std_inject::injected_crate_name(krate),
        sess: sess,
        parent_def: None,
        resolver: resolver,
        items: BTreeMap::new(),
        trait_items: BTreeMap::new(),
        impl_items: BTreeMap::new(),
        bodies: BTreeMap::new(),
        trait_impls: BTreeMap::new(),
        trait_default_impl: BTreeMap::new(),
        loop_scopes: Vec::new(),
        is_in_loop_condition: false,
        type_def_lifetime_params: DefIdMap(),
    }.lower_crate(krate)
}

#[derive(Copy, Clone, PartialEq, Eq)]
enum ParamMode {
    /// Any path in a type context.
    Explicit,
    /// The `module::Type` in `module::Type::method` in an expression.
    Optional
}

impl<'a> LoweringContext<'a> {
    fn lower_crate(mut self, c: &Crate) -> hir::Crate {
        /// Full-crate AST visitor that inserts into a fresh
        /// `LoweringContext` any information that may be
        /// needed from arbitrary locations in the crate.
        /// E.g. The number of lifetime generic parameters
        /// declared for every type and trait definition.
        struct MiscCollector<'lcx, 'interner: 'lcx> {
            lctx: &'lcx mut LoweringContext<'interner>,
        }

        impl<'lcx, 'interner> Visitor<'lcx> for MiscCollector<'lcx, 'interner> {
            fn visit_item(&mut self, item: &'lcx Item) {
                match item.node {
                    ItemKind::Struct(_, ref generics) |
                    ItemKind::Union(_, ref generics) |
                    ItemKind::Enum(_, ref generics) |
                    ItemKind::Ty(_, ref generics) |
                    ItemKind::Trait(_, ref generics, ..) => {
                        let def_id = self.lctx.resolver.definitions().local_def_id(item.id);
                        let count = generics.lifetimes.len();
                        self.lctx.type_def_lifetime_params.insert(def_id, count);
                    }
                    _ => {}
                }
                visit::walk_item(self, item);
            }
        }

        struct ItemLowerer<'lcx, 'interner: 'lcx> {
            lctx: &'lcx mut LoweringContext<'interner>,
        }

        impl<'lcx, 'interner> Visitor<'lcx> for ItemLowerer<'lcx, 'interner> {
            fn visit_item(&mut self, item: &'lcx Item) {
                let hir_item = self.lctx.lower_item(item);
                self.lctx.items.insert(item.id, hir_item);
                visit::walk_item(self, item);
            }

            fn visit_trait_item(&mut self, item: &'lcx TraitItem) {
                let id = hir::TraitItemId { node_id: item.id };
                let hir_item = self.lctx.lower_trait_item(item);
                self.lctx.trait_items.insert(id, hir_item);
                visit::walk_trait_item(self, item);
            }

            fn visit_impl_item(&mut self, item: &'lcx ImplItem) {
                let id = hir::ImplItemId { node_id: item.id };
                let hir_item = self.lctx.lower_impl_item(item);
                self.lctx.impl_items.insert(id, hir_item);
                visit::walk_impl_item(self, item);
            }
        }

        visit::walk_crate(&mut MiscCollector { lctx: &mut self }, c);
        visit::walk_crate(&mut ItemLowerer { lctx: &mut self }, c);

        let module = self.lower_mod(&c.module);
        let attrs = self.lower_attrs(&c.attrs);
        let exported_macros = c.exported_macros.iter().map(|m| self.lower_macro_def(m)).collect();
        let body_ids = body_ids(&self.bodies);

        hir::Crate {
            module: module,
            attrs: attrs,
            span: c.span,
            exported_macros: exported_macros,
            items: self.items,
            trait_items: self.trait_items,
            impl_items: self.impl_items,
            bodies: self.bodies,
            body_ids: body_ids,
            trait_impls: self.trait_impls,
            trait_default_impl: self.trait_default_impl,
        }
    }

    fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>)
                   -> hir::BodyId {
        let body = hir::Body {
            arguments: decl.map_or(hir_vec![], |decl| {
                decl.inputs.iter().map(|x| self.lower_arg(x)).collect()
            }),
            value: value
        };
        let id = body.id();
        self.bodies.insert(id, body);
        id
    }

    fn next_id(&self) -> NodeId {
        self.sess.next_node_id()
    }

    fn expect_full_def(&mut self, id: NodeId) -> Def {
        self.resolver.get_resolution(id).map_or(Def::Err, |pr| {
            if pr.unresolved_segments() != 0 {
                bug!("path not fully resolved: {:?}", pr);
            }
            pr.base_def()
        })
    }

    fn diagnostic(&self) -> &errors::Handler {
        self.sess.diagnostic()
    }

    fn str_to_ident(&self, s: &'static str) -> Name {
        Symbol::gensym(s)
    }

    fn allow_internal_unstable(&self, reason: &'static str, mut span: Span) -> Span {
        span.expn_id = self.sess.codemap().record_expansion(codemap::ExpnInfo {
            call_site: span,
            callee: codemap::NameAndSpan {
                format: codemap::CompilerDesugaring(Symbol::intern(reason)),
                span: Some(span),
                allow_internal_unstable: true,
            },
        });
        span
    }

    fn with_loop_scope<T, F>(&mut self, loop_id: NodeId, f: F) -> T
        where F: FnOnce(&mut LoweringContext) -> T
    {
        // We're no longer in the base loop's condition; we're in another loop.
        let was_in_loop_condition = self.is_in_loop_condition;
        self.is_in_loop_condition = false;

        let len = self.loop_scopes.len();
        self.loop_scopes.push(loop_id);

        let result = f(self);
        assert_eq!(len + 1, self.loop_scopes.len(),
            "Loop scopes should be added and removed in stack order");

        self.loop_scopes.pop().unwrap();

        self.is_in_loop_condition = was_in_loop_condition;

        result
    }

    fn with_loop_condition_scope<T, F>(&mut self, f: F) -> T
        where F: FnOnce(&mut LoweringContext) -> T
    {
        let was_in_loop_condition = self.is_in_loop_condition;
        self.is_in_loop_condition = true;

        let result = f(self);

        self.is_in_loop_condition = was_in_loop_condition;

        result
    }

    fn with_new_loop_scopes<T, F>(&mut self, f: F) -> T
        where F: FnOnce(&mut LoweringContext) -> T
    {
        let was_in_loop_condition = self.is_in_loop_condition;
        self.is_in_loop_condition = false;

        let loop_scopes = mem::replace(&mut self.loop_scopes, Vec::new());
        let result = f(self);
        mem::replace(&mut self.loop_scopes, loop_scopes);

        self.is_in_loop_condition = was_in_loop_condition;

        result
    }

    fn with_parent_def<T, F>(&mut self, parent_id: NodeId, f: F) -> T
        where F: FnOnce(&mut LoweringContext) -> T
    {
        let old_def = self.parent_def;
        self.parent_def = {
            let defs = self.resolver.definitions();
            Some(defs.opt_def_index(parent_id).unwrap())
        };

        let result = f(self);

        self.parent_def = old_def;
        result
    }

    fn def_key(&mut self, id: DefId) -> DefKey {
        if id.is_local() {
            self.resolver.definitions().def_key(id.index)
        } else {
            self.sess.cstore.def_key(id)
        }
    }

    fn lower_opt_sp_ident(&mut self, o_id: Option<Spanned<Ident>>) -> Option<Spanned<Name>> {
        o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name))
    }

    fn lower_destination(&mut self, destination: Option<(NodeId, Spanned<Ident>)>)
        -> hir::Destination
    {
        match destination {
            Some((id, label_ident)) => hir::Destination {
                ident: Some(label_ident),
                loop_id: if let Def::Label(loop_id) = self.expect_full_def(id) {
                    hir::LoopIdResult::Ok(loop_id)
                } else {
                    hir::LoopIdResult::Err(hir::LoopIdError::UnresolvedLabel)
                }
            },
            None => hir::Destination {
                ident: None,
                loop_id: self.loop_scopes.last().map(|innermost_loop_id| Ok(*innermost_loop_id))
                            .unwrap_or(Err(hir::LoopIdError::OutsideLoopScope)).into()
            }
        }
    }

    fn lower_attrs(&mut self, attrs: &Vec<Attribute>) -> hir::HirVec<Attribute> {
        attrs.clone().into()
    }

    fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
        hir::Arm {
            attrs: self.lower_attrs(&arm.attrs),
            pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
            guard: arm.guard.as_ref().map(|ref x| P(self.lower_expr(x))),
            body: P(self.lower_expr(&arm.body)),
        }
    }

    fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding {
        hir::TypeBinding {
            id: b.id,
            name: b.ident.name,
            ty: self.lower_ty(&b.ty),
            span: b.span,
        }
    }

    fn lower_ty(&mut self, t: &Ty) -> P<hir::Ty> {
        P(hir::Ty {
            id: t.id,
            node: match t.node {
                TyKind::Infer => hir::TyInfer,
                TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)),
                TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)),
                TyKind::Rptr(ref region, ref mt) => {
                    let span = Span { hi: t.span.lo, ..t.span };
                    let lifetime = match *region {
                        Some(ref lt) => self.lower_lifetime(lt),
                        None => self.elided_lifetime(span)
                    };
                    hir::TyRptr(lifetime, self.lower_mt(mt))
                }
                TyKind::BareFn(ref f) => {
                    hir::TyBareFn(P(hir::BareFnTy {
                        lifetimes: self.lower_lifetime_defs(&f.lifetimes),
                        unsafety: self.lower_unsafety(f.unsafety),
                        abi: f.abi,
                        decl: self.lower_fn_decl(&f.decl),
                    }))
                }
                TyKind::Never => hir::TyNever,
                TyKind::Tup(ref tys) => {
                    hir::TyTup(tys.iter().map(|ty| self.lower_ty(ty)).collect())
                }
                TyKind::Paren(ref ty) => {
                    return self.lower_ty(ty);
                }
                TyKind::Path(ref qself, ref path) => {
                    let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit);
                    return self.ty_path(t.id, t.span, qpath);
                }
                TyKind::ImplicitSelf => {
                    hir::TyPath(hir::QPath::Resolved(None, P(hir::Path {
                        def: self.expect_full_def(t.id),
                        segments: hir_vec![hir::PathSegment {
                            name: keywords::SelfType.name(),
                            parameters: hir::PathParameters::none()
                        }],
                        span: t.span,
                    })))
                }
                TyKind::Array(ref ty, ref length) => {
                    let length = self.lower_expr(length);
                    hir::TyArray(self.lower_ty(ty),
                                 self.record_body(length, None))
                }
                TyKind::Typeof(ref expr) => {
                    let expr = self.lower_expr(expr);
                    hir::TyTypeof(self.record_body(expr, None))
                }
                TyKind::TraitObject(ref bounds) => {
                    let mut lifetime_bound = None;
                    let bounds = bounds.iter().filter_map(|bound| {
                        match *bound {
                            TraitTyParamBound(ref ty, TraitBoundModifier::None) => {
                                Some(self.lower_poly_trait_ref(ty))
                            }
                            TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
                            RegionTyParamBound(ref lifetime) => {
                                lifetime_bound = Some(self.lower_lifetime(lifetime));
                                None
                            }
                        }
                    }).collect();
                    let lifetime_bound = lifetime_bound.unwrap_or_else(|| {
                        self.elided_lifetime(t.span)
                    });
                    hir::TyTraitObject(bounds, lifetime_bound)
                }
                TyKind::ImplTrait(ref bounds) => {
                    hir::TyImplTrait(self.lower_bounds(bounds))
                }
                TyKind::Mac(_) => panic!("TyMac should have been expanded by now."),
            },
            span: t.span,
        })
    }

    fn lower_foreign_mod(&mut self, fm: &ForeignMod) -> hir::ForeignMod {
        hir::ForeignMod {
            abi: fm.abi,
            items: fm.items.iter().map(|x| self.lower_foreign_item(x)).collect(),
        }
    }

    fn lower_variant(&mut self, v: &Variant) -> hir::Variant {
        Spanned {
            node: hir::Variant_ {
                name: v.node.name.name,
                attrs: self.lower_attrs(&v.node.attrs),
                data: self.lower_variant_data(&v.node.data),
                disr_expr: v.node.disr_expr.as_ref().map(|e| {
                    let e = self.lower_expr(e);
                    self.record_body(e, None)
                }),
            },
            span: v.span,
        }
    }

    fn lower_qpath(&mut self,
                   id: NodeId,
                   qself: &Option<QSelf>,
                   p: &Path,
                   param_mode: ParamMode)
                   -> hir::QPath {
        let qself_position = qself.as_ref().map(|q| q.position);
        let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty));

        let resolution = self.resolver.get_resolution(id)
                                      .unwrap_or(PathResolution::new(Def::Err));

        let proj_start = p.segments.len() - resolution.unresolved_segments();
        let path = P(hir::Path {
            def: resolution.base_def(),
            segments: p.segments[..proj_start].iter().enumerate().map(|(i, segment)| {
                let param_mode = match (qself_position, param_mode) {
                    (Some(j), ParamMode::Optional) if i < j => {
                        // This segment is part of the trait path in a
                        // qualified path - one of `a`, `b` or `Trait`
                        // in `<X as a::b::Trait>::T::U::method`.
                        ParamMode::Explicit
                    }
                    _ => param_mode
                };

                // Figure out if this is a type/trait segment,
                // which may need lifetime elision performed.
                let parent_def_id = |this: &mut Self, def_id: DefId| {
                    DefId {
                        krate: def_id.krate,
                        index: this.def_key(def_id).parent.expect("missing parent")
                    }
                };
                let type_def_id = match resolution.base_def() {
                    Def::AssociatedTy(def_id) if i + 2 == proj_start => {
                        Some(parent_def_id(self, def_id))
                    }
                    Def::Variant(def_id) if i + 1 == proj_start => {
                        Some(parent_def_id(self, def_id))
                    }
                    Def::Struct(def_id) |
                    Def::Union(def_id) |
                    Def::Enum(def_id) |
                    Def::TyAlias(def_id) |
                    Def::Trait(def_id) if i + 1 == proj_start => Some(def_id),
                    _ => None
                };

                let num_lifetimes = type_def_id.map_or(0, |def_id| {
                    if let Some(&n) = self.type_def_lifetime_params.get(&def_id) {
                        return n;
                    }
                    assert!(!def_id.is_local());
                    let n = self.sess.cstore.item_generics_cloned(def_id).regions.len();
                    self.type_def_lifetime_params.insert(def_id, n);
                    n
                });
                self.lower_path_segment(p.span, segment, param_mode, num_lifetimes)
            }).collect(),
            span: p.span,
        });

        // Simple case, either no projections, or only fully-qualified.
        // E.g. `std::mem::size_of` or `<I as Iterator>::Item`.
        if resolution.unresolved_segments() == 0 {
            return hir::QPath::Resolved(qself, path);
        }

        // Create the innermost type that we're projecting from.
        let mut ty = if path.segments.is_empty() {
            // If the base path is empty that means there exists a
            // syntactical `Self`, e.g. `&i32` in `<&i32>::clone`.
            qself.expect("missing QSelf for <T>::...")
        } else {
            // Otherwise, the base path is an implicit `Self` type path,
            // e.g. `Vec` in `Vec::new` or `<I as Iterator>::Item` in
            // `<I as Iterator>::Item::default`.
            let new_id = self.next_id();
            self.ty_path(new_id, p.span, hir::QPath::Resolved(qself, path))
        };

        // Anything after the base path are associated "extensions",
        // out of which all but the last one are associated types,
        // e.g. for `std::vec::Vec::<T>::IntoIter::Item::clone`:
        // * base path is `std::vec::Vec<T>`
        // * "extensions" are `IntoIter`, `Item` and `clone`
        // * type nodes are:
        //   1. `std::vec::Vec<T>` (created above)
        //   2. `<std::vec::Vec<T>>::IntoIter`
        //   3. `<<std::vec::Vec<T>>::IntoIter>::Item`
        // * final path is `<<<std::vec::Vec<T>>::IntoIter>::Item>::clone`
        for (i, segment) in p.segments.iter().enumerate().skip(proj_start) {
            let segment = P(self.lower_path_segment(p.span, segment, param_mode, 0));
            let qpath = hir::QPath::TypeRelative(ty, segment);

            // It's finished, return the extension of the right node type.
            if i == p.segments.len() - 1 {
                return qpath;
            }

            // Wrap the associated extension in another type node.
            let new_id = self.next_id();
            ty = self.ty_path(new_id, p.span, qpath);
        }

        // Should've returned in the for loop above.
        span_bug!(p.span, "lower_qpath: no final extension segment in {}..{}",
                  proj_start, p.segments.len())
    }

    fn lower_path_extra(&mut self,
                        id: NodeId,
                        p: &Path,
                        name: Option<Name>,
                        param_mode: ParamMode,
                        defaults_to_global: bool)
                        -> hir::Path {
        let mut segments = p.segments.iter();
        if defaults_to_global && p.is_global() {
            segments.next();
        }

        hir::Path {
            def: self.expect_full_def(id),
            segments: segments.map(|segment| {
                self.lower_path_segment(p.span, segment, param_mode, 0)
            }).chain(name.map(|name| {
                hir::PathSegment {
                    name: name,
                    parameters: hir::PathParameters::none()
                }
            })).collect(),
            span: p.span,
        }
    }

    fn lower_path(&mut self,
                  id: NodeId,
                  p: &Path,
                  param_mode: ParamMode,
                  defaults_to_global: bool)
                  -> hir::Path {
        self.lower_path_extra(id, p, None, param_mode, defaults_to_global)
    }

    fn lower_path_segment(&mut self,
                          path_span: Span,
                          segment: &PathSegment,
                          param_mode: ParamMode,
                          expected_lifetimes: usize)
                          -> hir::PathSegment {
        let mut parameters = if let Some(ref parameters) = segment.parameters {
            match **parameters {
                PathParameters::AngleBracketed(ref data) => {
                    let data = self.lower_angle_bracketed_parameter_data(data, param_mode);
                    hir::AngleBracketedParameters(data)
                }
                PathParameters::Parenthesized(ref data) => {
                    hir::ParenthesizedParameters(self.lower_parenthesized_parameter_data(data))
                }
            }
        } else {
            let data = self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode);
            hir::AngleBracketedParameters(data)
        };

        if let hir::AngleBracketedParameters(ref mut data) = parameters {
            if data.lifetimes.is_empty() {
                data.lifetimes = (0..expected_lifetimes).map(|_| {
                    self.elided_lifetime(path_span)
                }).collect();
            }
        }

        hir::PathSegment {
            name: segment.identifier.name,
            parameters: parameters,
        }
    }

    fn lower_angle_bracketed_parameter_data(&mut self,
                                            data: &AngleBracketedParameterData,
                                            param_mode: ParamMode)
                                            -> hir::AngleBracketedParameterData {
        let &AngleBracketedParameterData { ref lifetimes, ref types, ref bindings } = data;
        hir::AngleBracketedParameterData {
            lifetimes: self.lower_lifetimes(lifetimes),
            types: types.iter().map(|ty| self.lower_ty(ty)).collect(),
            infer_types: types.is_empty() && param_mode == ParamMode::Optional,
            bindings: bindings.iter().map(|b| self.lower_ty_binding(b)).collect(),
        }
    }

    fn lower_parenthesized_parameter_data(&mut self,
                                          data: &ParenthesizedParameterData)
                                          -> hir::ParenthesizedParameterData {
        let &ParenthesizedParameterData { ref inputs, ref output, span } = data;
        hir::ParenthesizedParameterData {
            inputs: inputs.iter().map(|ty| self.lower_ty(ty)).collect(),
            output: output.as_ref().map(|ty| self.lower_ty(ty)),
            span: span,
        }
    }

    fn lower_local(&mut self, l: &Local) -> P<hir::Local> {
        P(hir::Local {
            id: l.id,
            ty: l.ty.as_ref().map(|t| self.lower_ty(t)),
            pat: self.lower_pat(&l.pat),
            init: l.init.as_ref().map(|e| P(self.lower_expr(e))),
            span: l.span,
            attrs: l.attrs.clone(),
        })
    }

    fn lower_mutability(&mut self, m: Mutability) -> hir::Mutability {
        match m {
            Mutability::Mutable => hir::MutMutable,
            Mutability::Immutable => hir::MutImmutable,
        }
    }

    fn lower_arg(&mut self, arg: &Arg) -> hir::Arg {
        hir::Arg {
            id: arg.id,
            pat: self.lower_pat(&arg.pat),
        }
    }

    fn lower_fn_args_to_names(&mut self, decl: &FnDecl)
                              -> hir::HirVec<Spanned<Name>> {
        decl.inputs.iter().map(|arg| {
            match arg.pat.node {
                PatKind::Ident(_, ident, None) => {
                    respan(ident.span, ident.node.name)
                }
                _ => respan(arg.pat.span, keywords::Invalid.name()),
            }
        }).collect()
    }

    fn lower_fn_decl(&mut self, decl: &FnDecl) -> P<hir::FnDecl> {
        P(hir::FnDecl {
            inputs: decl.inputs.iter().map(|arg| self.lower_ty(&arg.ty)).collect(),
            output: match decl.output {
                FunctionRetTy::Ty(ref ty) => hir::Return(self.lower_ty(ty)),
                FunctionRetTy::Default(span) => hir::DefaultReturn(span),
            },
            variadic: decl.variadic,
        })
    }

    fn lower_ty_param_bound(&mut self, tpb: &TyParamBound) -> hir::TyParamBound {
        match *tpb {
            TraitTyParamBound(ref ty, modifier) => {
                hir::TraitTyParamBound(self.lower_poly_trait_ref(ty),
                                       self.lower_trait_bound_modifier(modifier))
            }
            RegionTyParamBound(ref lifetime) => {
                hir::RegionTyParamBound(self.lower_lifetime(lifetime))
            }
        }
    }

    fn lower_ty_param(&mut self, tp: &TyParam, add_bounds: &[TyParamBound]) -> hir::TyParam {
        let mut name = tp.ident.name;

        // Don't expose `Self` (recovered "keyword used as ident" parse error).
        // `rustc::ty` expects `Self` to be only used for a trait's `Self`.
        // Instead, use gensym("Self") to create a distinct name that looks the same.
        if name == keywords::SelfType.name() {
            name = Symbol::gensym("Self");
        }

        let mut bounds = self.lower_bounds(&tp.bounds);
        if !add_bounds.is_empty() {
            bounds = bounds.into_iter().chain(self.lower_bounds(add_bounds).into_iter()).collect();
        }

        hir::TyParam {
            id: tp.id,
            name: name,
            bounds: bounds,
            default: tp.default.as_ref().map(|x| self.lower_ty(x)),
            span: tp.span,
            pure_wrt_drop: tp.attrs.iter().any(|attr| attr.check_name("may_dangle")),
        }
    }

    fn lower_ty_params(&mut self, tps: &Vec<TyParam>, add_bounds: &NodeMap<Vec<TyParamBound>>)
                       -> hir::HirVec<hir::TyParam> {
        tps.iter().map(|tp| {
            self.lower_ty_param(tp, add_bounds.get(&tp.id).map_or(&[][..], |x| &x))
        }).collect()
    }

    fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
        hir::Lifetime {
            id: l.id,
            name: l.name,
            span: l.span,
        }
    }

    fn lower_lifetime_def(&mut self, l: &LifetimeDef) -> hir::LifetimeDef {
        hir::LifetimeDef {
            lifetime: self.lower_lifetime(&l.lifetime),
            bounds: self.lower_lifetimes(&l.bounds),
            pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")),
        }
    }

    fn lower_lifetimes(&mut self, lts: &Vec<Lifetime>) -> hir::HirVec<hir::Lifetime> {
        lts.iter().map(|l| self.lower_lifetime(l)).collect()
    }

    fn lower_lifetime_defs(&mut self, lts: &Vec<LifetimeDef>) -> hir::HirVec<hir::LifetimeDef> {
        lts.iter().map(|l| self.lower_lifetime_def(l)).collect()
    }

    fn lower_generics(&mut self, g: &Generics) -> hir::Generics {
        // Collect `?Trait` bounds in where clause and move them to parameter definitions.
        let mut add_bounds = NodeMap();
        for pred in &g.where_clause.predicates {
            if let WherePredicate::BoundPredicate(ref bound_pred) = *pred {
                'next_bound: for bound in &bound_pred.bounds {
                    if let TraitTyParamBound(_, TraitBoundModifier::Maybe) = *bound {
                        let report_error = |this: &mut Self| {
                            this.diagnostic().span_err(bound_pred.bounded_ty.span,
                                                       "`?Trait` bounds are only permitted at the \
                                                        point where a type parameter is declared");
                        };
                        // Check if the where clause type is a plain type parameter.
                        match bound_pred.bounded_ty.node {
                            TyKind::Path(None, ref path)
                                    if path.segments.len() == 1 &&
                                       bound_pred.bound_lifetimes.is_empty() => {
                                if let Some(Def::TyParam(def_id)) =
                                        self.resolver.get_resolution(bound_pred.bounded_ty.id)
                                                     .map(|d| d.base_def()) {
                                    if let Some(node_id) =
                                            self.resolver.definitions().as_local_node_id(def_id) {
                                        for ty_param in &g.ty_params {
                                            if node_id == ty_param.id {
                                                add_bounds.entry(ty_param.id).or_insert(Vec::new())
                                                                            .push(bound.clone());
                                                continue 'next_bound;
                                            }
                                        }
                                    }
                                }
                                report_error(self)
                            }
                            _ => report_error(self)
                        }
                    }
                }
            }
        }

        hir::Generics {
            ty_params: self.lower_ty_params(&g.ty_params, &add_bounds),
            lifetimes: self.lower_lifetime_defs(&g.lifetimes),
            where_clause: self.lower_where_clause(&g.where_clause),
            span: g.span,
        }
    }

    fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause {
        hir::WhereClause {
            id: wc.id,
            predicates: wc.predicates
                          .iter()
                          .map(|predicate| self.lower_where_predicate(predicate))
                          .collect(),
        }
    }

    fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate {
        match *pred {
            WherePredicate::BoundPredicate(WhereBoundPredicate{ ref bound_lifetimes,
                                                                ref bounded_ty,
                                                                ref bounds,
                                                                span}) => {
                hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
                    bound_lifetimes: self.lower_lifetime_defs(bound_lifetimes),
                    bounded_ty: self.lower_ty(bounded_ty),
                    bounds: bounds.iter().filter_map(|bound| match *bound {
                        // Ignore `?Trait` bounds, they were copied into type parameters already.
                        TraitTyParamBound(_, TraitBoundModifier::Maybe) => None,
                        _ => Some(self.lower_ty_param_bound(bound))
                    }).collect(),
                    span: span,
                })
            }
            WherePredicate::RegionPredicate(WhereRegionPredicate{ ref lifetime,
                                                                  ref bounds,
                                                                  span}) => {
                hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
                    span: span,
                    lifetime: self.lower_lifetime(lifetime),
                    bounds: bounds.iter().map(|bound| self.lower_lifetime(bound)).collect(),
                })
            }
            WherePredicate::EqPredicate(WhereEqPredicate{ id,
                                                          ref lhs_ty,
                                                          ref rhs_ty,
                                                          span}) => {
                hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
                    id: id,
                    lhs_ty: self.lower_ty(lhs_ty),
                    rhs_ty: self.lower_ty(rhs_ty),
                    span: span,
                })
            }
        }
    }

    fn lower_variant_data(&mut self, vdata: &VariantData) -> hir::VariantData {
        match *vdata {
            VariantData::Struct(ref fields, id) => {
                hir::VariantData::Struct(fields.iter()
                                               .enumerate()
                                               .map(|f| self.lower_struct_field(f))
                                               .collect(),
                                         id)
            }
            VariantData::Tuple(ref fields, id) => {
                hir::VariantData::Tuple(fields.iter()
                                              .enumerate()
                                              .map(|f| self.lower_struct_field(f))
                                              .collect(),
                                        id)
            }
            VariantData::Unit(id) => hir::VariantData::Unit(id),
        }
    }

    fn lower_trait_ref(&mut self, p: &TraitRef) -> hir::TraitRef {
        let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit) {
            hir::QPath::Resolved(None, path) => path.and_then(|path| path),
            qpath => bug!("lower_trait_ref: unexpected QPath `{:?}`", qpath)
        };
        hir::TraitRef {
            path: path,
            ref_id: p.ref_id,
        }
    }

    fn lower_poly_trait_ref(&mut self, p: &PolyTraitRef) -> hir::PolyTraitRef {
        hir::PolyTraitRef {
            bound_lifetimes: self.lower_lifetime_defs(&p.bound_lifetimes),
            trait_ref: self.lower_trait_ref(&p.trait_ref),
            span: p.span,
        }
    }

    fn lower_struct_field(&mut self, (index, f): (usize, &StructField)) -> hir::StructField {
        hir::StructField {
            span: f.span,
            id: f.id,
            name: f.ident.map(|ident| ident.name).unwrap_or(Symbol::intern(&index.to_string())),
            vis: self.lower_visibility(&f.vis),
            ty: self.lower_ty(&f.ty),
            attrs: self.lower_attrs(&f.attrs),
        }
    }

    fn lower_field(&mut self, f: &Field) -> hir::Field {
        hir::Field {
            name: respan(f.ident.span, f.ident.node.name),
            expr: P(self.lower_expr(&f.expr)),
            span: f.span,
            is_shorthand: f.is_shorthand,
        }
    }

    fn lower_mt(&mut self, mt: &MutTy) -> hir::MutTy {
        hir::MutTy {
            ty: self.lower_ty(&mt.ty),
            mutbl: self.lower_mutability(mt.mutbl),
        }
    }

    fn lower_bounds(&mut self, bounds: &[TyParamBound]) -> hir::TyParamBounds {
        bounds.iter().map(|bound| self.lower_ty_param_bound(bound)).collect()
    }

    fn lower_block(&mut self, b: &Block) -> P<hir::Block> {
        let mut expr = None;

        let mut stmts = b.stmts.iter().flat_map(|s| self.lower_stmt(s)).collect::<Vec<_>>();
        if let Some(last) = stmts.pop() {
            if let hir::StmtExpr(e, _) = last.node {
                expr = Some(e);
            } else {
                stmts.push(last);
            }
        }

        P(hir::Block {
            id: b.id,
            stmts: stmts.into(),
            expr: expr,
            rules: self.lower_block_check_mode(&b.rules),
            span: b.span,
        })
    }

    fn lower_item_kind(&mut self,
                       id: NodeId,
                       name: &mut Name,
                       attrs: &hir::HirVec<Attribute>,
                       vis: &mut hir::Visibility,
                       i: &ItemKind)
                       -> hir::Item_ {
        match *i {
            ItemKind::ExternCrate(string) => hir::ItemExternCrate(string),
            ItemKind::Use(ref view_path) => {
                let path = match view_path.node {
                    ViewPathSimple(_, ref path) => path,
                    ViewPathGlob(ref path) => path,
                    ViewPathList(ref path, ref path_list_idents) => {
                        for &Spanned { node: ref import, span } in path_list_idents {
                            // `use a::{self as x, b as y};` lowers to
                            // `use a as x; use a::b as y;`
                            let mut ident = import.name;
                            let suffix = if ident.name == keywords::SelfValue.name() {
                                if let Some(last) = path.segments.last() {
                                    ident = last.identifier;
                                }
                                None
                            } else {
                                Some(ident.name)
                            };

                            let mut path = self.lower_path_extra(import.id, path, suffix,
                                                                 ParamMode::Explicit, true);
                            path.span = span;
                            self.items.insert(import.id, hir::Item {
                                id: import.id,
                                name: import.rename.unwrap_or(ident).name,
                                attrs: attrs.clone(),
                                node: hir::ItemUse(P(path), hir::UseKind::Single),
                                vis: vis.clone(),
                                span: span,
                            });
                        }
                        path
                    }
                };
                let path = P(self.lower_path(id, path, ParamMode::Explicit, true));
                let kind = match view_path.node {
                    ViewPathSimple(ident, _) => {
                        *name = ident.name;
                        hir::UseKind::Single
                    }
                    ViewPathGlob(_) => {
                        hir::UseKind::Glob
                    }
                    ViewPathList(..) => {
                        // Privatize the degenerate import base, used only to check
                        // the stability of `use a::{};`, to avoid it showing up as
                        // a reexport by accident when `pub`, e.g. in documentation.
                        *vis = hir::Inherited;
                        hir::UseKind::ListStem
                    }
                };
                hir::ItemUse(path, kind)
            }
            ItemKind::Static(ref t, m, ref e) => {
                let value = self.lower_expr(e);
                hir::ItemStatic(self.lower_ty(t),
                                self.lower_mutability(m),
                                self.record_body(value, None))
            }
            ItemKind::Const(ref t, ref e) => {
                let value = self.lower_expr(e);
                hir::ItemConst(self.lower_ty(t),
                               self.record_body(value, None))
            }
            ItemKind::Fn(ref decl, unsafety, constness, abi, ref generics, ref body) => {
                self.with_new_loop_scopes(|this| {
                    let body = this.lower_block(body);
                    let body = this.expr_block(body, ThinVec::new());
                    let body_id = this.record_body(body, Some(decl));
                    hir::ItemFn(this.lower_fn_decl(decl),
                                              this.lower_unsafety(unsafety),
                                              this.lower_constness(constness),
                                              abi,
                                              this.lower_generics(generics),
                                              body_id)
                })
            }
            ItemKind::Mod(ref m) => hir::ItemMod(self.lower_mod(m)),
            ItemKind::ForeignMod(ref nm) => hir::ItemForeignMod(self.lower_foreign_mod(nm)),
            ItemKind::Ty(ref t, ref generics) => {
                hir::ItemTy(self.lower_ty(t), self.lower_generics(generics))
            }
            ItemKind::Enum(ref enum_definition, ref generics) => {
                hir::ItemEnum(hir::EnumDef {
                                  variants: enum_definition.variants
                                                           .iter()
                                                           .map(|x| self.lower_variant(x))
                                                           .collect(),
                              },
                              self.lower_generics(generics))
            }
            ItemKind::Struct(ref struct_def, ref generics) => {
                let struct_def = self.lower_variant_data(struct_def);
                hir::ItemStruct(struct_def, self.lower_generics(generics))
            }
            ItemKind::Union(ref vdata, ref generics) => {
                let vdata = self.lower_variant_data(vdata);
                hir::ItemUnion(vdata, self.lower_generics(generics))
            }
            ItemKind::DefaultImpl(unsafety, ref trait_ref) => {
                let trait_ref = self.lower_trait_ref(trait_ref);

                if let Def::Trait(def_id) = trait_ref.path.def {
                    self.trait_default_impl.insert(def_id, id);
                }

                hir::ItemDefaultImpl(self.lower_unsafety(unsafety),
                                     trait_ref)
            }
            ItemKind::Impl(unsafety, polarity, ref generics, ref ifce, ref ty, ref impl_items) => {
                let new_impl_items = impl_items.iter()
                                               .map(|item| self.lower_impl_item_ref(item))
                                               .collect();
                let ifce = ifce.as_ref().map(|trait_ref| self.lower_trait_ref(trait_ref));

                if let Some(ref trait_ref) = ifce {
                    if let Def::Trait(def_id) = trait_ref.path.def {
                        self.trait_impls.entry(def_id).or_insert(vec![]).push(id);
                    }
                }

                hir::ItemImpl(self.lower_unsafety(unsafety),
                              self.lower_impl_polarity(polarity),
                              self.lower_generics(generics),
                              ifce,
                              self.lower_ty(ty),
                              new_impl_items)
            }
            ItemKind::Trait(unsafety, ref generics, ref bounds, ref items) => {
                let bounds = self.lower_bounds(bounds);
                let items = items.iter().map(|item| self.lower_trait_item_ref(item)).collect();
                hir::ItemTrait(self.lower_unsafety(unsafety),
                               self.lower_generics(generics),
                               bounds,
                               items)
            }
            ItemKind::Mac(_) => panic!("Shouldn't still be around"),
        }
    }

    fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem {
        self.with_parent_def(i.id, |this| {
            hir::TraitItem {
                id: i.id,
                name: i.ident.name,
                attrs: this.lower_attrs(&i.attrs),
                node: match i.node {
                    TraitItemKind::Const(ref ty, ref default) => {
                        hir::TraitItemKind::Const(this.lower_ty(ty),
                                                  default.as_ref().map(|x| {
                            let value = this.lower_expr(x);
                            this.record_body(value, None)
                        }))
                    }
                    TraitItemKind::Method(ref sig, None) => {
                        let names = this.lower_fn_args_to_names(&sig.decl);
                        hir::TraitItemKind::Method(this.lower_method_sig(sig),
                                                   hir::TraitMethod::Required(names))
                    }
                    TraitItemKind::Method(ref sig, Some(ref body)) => {
                        let body = this.lower_block(body);
                        let expr = this.expr_block(body, ThinVec::new());
                        let body_id = this.record_body(expr, Some(&sig.decl));
                        hir::TraitItemKind::Method(this.lower_method_sig(sig),
                                                   hir::TraitMethod::Provided(body_id))
                    }
                    TraitItemKind::Type(ref bounds, ref default) => {
                        hir::TraitItemKind::Type(this.lower_bounds(bounds),
                                                 default.as_ref().map(|x| this.lower_ty(x)))
                    }
                    TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"),
                },
                span: i.span,
            }
        })
    }

    fn lower_trait_item_ref(&mut self, i: &TraitItem) -> hir::TraitItemRef {
        let (kind, has_default) = match i.node {
            TraitItemKind::Const(_, ref default) => {
                (hir::AssociatedItemKind::Const, default.is_some())
            }
            TraitItemKind::Type(_, ref default) => {
                (hir::AssociatedItemKind::Type, default.is_some())
            }
            TraitItemKind::Method(ref sig, ref default) => {
                (hir::AssociatedItemKind::Method {
                    has_self: sig.decl.has_self(),
                 }, default.is_some())
            }
            TraitItemKind::Macro(..) => unimplemented!(),
        };
        hir::TraitItemRef {
            id: hir::TraitItemId { node_id: i.id },
            name: i.ident.name,
            span: i.span,
            defaultness: self.lower_defaultness(Defaultness::Default, has_default),
            kind: kind,
        }
    }

    fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem {
        self.with_parent_def(i.id, |this| {
            hir::ImplItem {
                id: i.id,
                name: i.ident.name,
                attrs: this.lower_attrs(&i.attrs),
                vis: this.lower_visibility(&i.vis),
                defaultness: this.lower_defaultness(i.defaultness, true /* [1] */),
                node: match i.node {
                    ImplItemKind::Const(ref ty, ref expr) => {
                        let value = this.lower_expr(expr);
                        let body_id = this.record_body(value, None);
                        hir::ImplItemKind::Const(this.lower_ty(ty), body_id)
                    }
                    ImplItemKind::Method(ref sig, ref body) => {
                        let body = this.lower_block(body);
                        let expr = this.expr_block(body, ThinVec::new());
                        let body_id = this.record_body(expr, Some(&sig.decl));
                        hir::ImplItemKind::Method(this.lower_method_sig(sig), body_id)
                    }
                    ImplItemKind::Type(ref ty) => hir::ImplItemKind::Type(this.lower_ty(ty)),
                    ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"),
                },
                span: i.span,
            }
        })

        // [1] since `default impl` is not yet implemented, this is always true in impls
    }

    fn lower_impl_item_ref(&mut self, i: &ImplItem) -> hir::ImplItemRef {
        hir::ImplItemRef {
            id: hir::ImplItemId { node_id: i.id },
            name: i.ident.name,
            span: i.span,
            vis: self.lower_visibility(&i.vis),
            defaultness: self.lower_defaultness(i.defaultness, true /* [1] */),
            kind: match i.node {
                ImplItemKind::Const(..) => hir::AssociatedItemKind::Const,
                ImplItemKind::Type(..) => hir::AssociatedItemKind::Type,
                ImplItemKind::Method(ref sig, _) => hir::AssociatedItemKind::Method {
                    has_self: sig.decl.has_self(),
                },
                ImplItemKind::Macro(..) => unimplemented!(),
            },
        }

        // [1] since `default impl` is not yet implemented, this is always true in impls
    }

    fn lower_mod(&mut self, m: &Mod) -> hir::Mod {
        hir::Mod {
            inner: m.inner,
            item_ids: m.items.iter().flat_map(|x| self.lower_item_id(x)).collect(),
        }
    }

    fn lower_macro_def(&mut self, m: &MacroDef) -> hir::MacroDef {
        hir::MacroDef {
            name: m.ident.name,
            attrs: self.lower_attrs(&m.attrs),
            id: m.id,
            span: m.span,
            body: m.body.clone().into(),
        }
    }

    fn lower_item_id(&mut self, i: &Item) -> SmallVector<hir::ItemId> {
        if let ItemKind::Use(ref view_path) = i.node {
            if let ViewPathList(_, ref imports) = view_path.node {
                return iter::once(i.id).chain(imports.iter().map(|import| import.node.id))
                    .map(|id| hir::ItemId { id: id }).collect();
            }
        }
        SmallVector::one(hir::ItemId { id: i.id })
    }

    pub fn lower_item(&mut self, i: &Item) -> hir::Item {
        let mut name = i.ident.name;
        let attrs = self.lower_attrs(&i.attrs);
        let mut vis = self.lower_visibility(&i.vis);
        let node = self.with_parent_def(i.id, |this| {
            this.lower_item_kind(i.id, &mut name, &attrs, &mut vis, &i.node)
        });

        hir::Item {
            id: i.id,
            name: name,
            attrs: attrs,
            node: node,
            vis: vis,
            span: i.span,
        }
    }

    fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem {
        self.with_parent_def(i.id, |this| {
            hir::ForeignItem {
                id: i.id,
                name: i.ident.name,
                attrs: this.lower_attrs(&i.attrs),
                node: match i.node {
                    ForeignItemKind::Fn(ref fdec, ref generics) => {
                        hir::ForeignItemFn(this.lower_fn_decl(fdec),
                                           this.lower_fn_args_to_names(fdec),
                                           this.lower_generics(generics))
                    }
                    ForeignItemKind::Static(ref t, m) => {
                        hir::ForeignItemStatic(this.lower_ty(t), m)
                    }
                },
                vis: this.lower_visibility(&i.vis),
                span: i.span,
            }
        })
    }

    fn lower_method_sig(&mut self, sig: &MethodSig) -> hir::MethodSig {
        hir::MethodSig {
            generics: self.lower_generics(&sig.generics),
            abi: sig.abi,
            unsafety: self.lower_unsafety(sig.unsafety),
            constness: self.lower_constness(sig.constness),
            decl: self.lower_fn_decl(&sig.decl),
        }
    }

    fn lower_unsafety(&mut self, u: Unsafety) -> hir::Unsafety {
        match u {
            Unsafety::Unsafe => hir::Unsafety::Unsafe,
            Unsafety::Normal => hir::Unsafety::Normal,
        }
    }

    fn lower_constness(&mut self, c: Spanned<Constness>) -> hir::Constness {
        match c.node {
            Constness::Const => hir::Constness::Const,
            Constness::NotConst => hir::Constness::NotConst,
        }
    }

    fn lower_unop(&mut self, u: UnOp) -> hir::UnOp {
        match u {
            UnOp::Deref => hir::UnDeref,
            UnOp::Not => hir::UnNot,
            UnOp::Neg => hir::UnNeg,
        }
    }

    fn lower_binop(&mut self, b: BinOp) -> hir::BinOp {
        Spanned {
            node: match b.node {
                BinOpKind::Add => hir::BiAdd,
                BinOpKind::Sub => hir::BiSub,
                BinOpKind::Mul => hir::BiMul,
                BinOpKind::Div => hir::BiDiv,
                BinOpKind::Rem => hir::BiRem,
                BinOpKind::And => hir::BiAnd,
                BinOpKind::Or => hir::BiOr,
                BinOpKind::BitXor => hir::BiBitXor,
                BinOpKind::BitAnd => hir::BiBitAnd,
                BinOpKind::BitOr => hir::BiBitOr,
                BinOpKind::Shl => hir::BiShl,
                BinOpKind::Shr => hir::BiShr,
                BinOpKind::Eq => hir::BiEq,
                BinOpKind::Lt => hir::BiLt,
                BinOpKind::Le => hir::BiLe,
                BinOpKind::Ne => hir::BiNe,
                BinOpKind::Ge => hir::BiGe,
                BinOpKind::Gt => hir::BiGt,
            },
            span: b.span,
        }
    }

    fn lower_pat(&mut self, p: &Pat) -> P<hir::Pat> {
        P(hir::Pat {
            id: p.id,
            node: match p.node {
                PatKind::Wild => hir::PatKind::Wild,
                PatKind::Ident(ref binding_mode, pth1, ref sub) => {
                    self.with_parent_def(p.id, |this| {
                        match this.resolver.get_resolution(p.id).map(|d| d.base_def()) {
                            // `None` can occur in body-less function signatures
                            def @ None | def @ Some(Def::Local(_)) => {
                                let def_id = def.map(|d| d.def_id()).unwrap_or_else(|| {
                                    this.resolver.definitions().local_def_id(p.id)
                                });
                                hir::PatKind::Binding(this.lower_binding_mode(binding_mode),
                                                      def_id,
                                                      respan(pth1.span, pth1.node.name),
                                                      sub.as_ref().map(|x| this.lower_pat(x)))
                            }
                            Some(def) => {
                                hir::PatKind::Path(hir::QPath::Resolved(None, P(hir::Path {
                                    span: pth1.span,
                                    def: def,
                                    segments: hir_vec![
                                        hir::PathSegment::from_name(pth1.node.name)
                                    ],
                                })))
                            }
                        }
                    })
                }
                PatKind::Lit(ref e) => hir::PatKind::Lit(P(self.lower_expr(e))),
                PatKind::TupleStruct(ref path, ref pats, ddpos) => {
                    let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional);
                    hir::PatKind::TupleStruct(qpath,
                                              pats.iter().map(|x| self.lower_pat(x)).collect(),
                                              ddpos)
                }
                PatKind::Path(ref qself, ref path) => {
                    hir::PatKind::Path(self.lower_qpath(p.id, qself, path, ParamMode::Optional))
                }
                PatKind::Struct(ref path, ref fields, etc) => {
                    let qpath = self.lower_qpath(p.id, &None, path, ParamMode::Optional);

                    let fs = fields.iter()
                                   .map(|f| {
                                       Spanned {
                                           span: f.span,
                                           node: hir::FieldPat {
                                               name: f.node.ident.name,
                                               pat: self.lower_pat(&f.node.pat),
                                               is_shorthand: f.node.is_shorthand,
                                           },
                                       }
                                   })
                                   .collect();
                    hir::PatKind::Struct(qpath, fs, etc)
                }
                PatKind::Tuple(ref elts, ddpos) => {
                    hir::PatKind::Tuple(elts.iter().map(|x| self.lower_pat(x)).collect(), ddpos)
                }
                PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
                PatKind::Ref(ref inner, mutbl) => {
                    hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
                }
                PatKind::Range(ref e1, ref e2, ref end) => {
                    hir::PatKind::Range(P(self.lower_expr(e1)),
                                        P(self.lower_expr(e2)),
                                        self.lower_range_end(end))
                }
                PatKind::Slice(ref before, ref slice, ref after) => {
                    hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
                                slice.as_ref().map(|x| self.lower_pat(x)),
                                after.iter().map(|x| self.lower_pat(x)).collect())
                }
                PatKind::Mac(_) => panic!("Shouldn't exist here"),
            },
            span: p.span,
        })
    }

    fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
        match *e {
            RangeEnd::Included => hir::RangeEnd::Included,
            RangeEnd::Excluded => hir::RangeEnd::Excluded,
        }
    }

    fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
        hir::Expr {
            id: e.id,
            node: match e.node {
                // Issue #22181:
                // Eventually a desugaring for `box EXPR`
                // (similar to the desugaring above for `in PLACE BLOCK`)
                // should go here, desugaring
                //
                // to:
                //
                // let mut place = BoxPlace::make_place();
                // let raw_place = Place::pointer(&mut place);
                // let value = $value;
                // unsafe {
                //     ::std::ptr::write(raw_place, value);
                //     Boxed::finalize(place)
                // }
                //
                // But for now there are type-inference issues doing that.
                ExprKind::Box(ref e) => {
                    hir::ExprBox(P(self.lower_expr(e)))
                }

                // Desugar ExprBox: `in (PLACE) EXPR`
                ExprKind::InPlace(ref placer, ref value_expr) => {
                    // to:
                    //
                    // let p = PLACE;
                    // let mut place = Placer::make_place(p);
                    // let raw_place = Place::pointer(&mut place);
                    // push_unsafe!({
                    //     std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
                    //     InPlace::finalize(place)
                    // })
                    let placer_expr = P(self.lower_expr(placer));
                    let value_expr = P(self.lower_expr(value_expr));

                    let placer_ident = self.str_to_ident("placer");
                    let place_ident = self.str_to_ident("place");
                    let p_ptr_ident = self.str_to_ident("p_ptr");

                    let make_place = ["ops", "Placer", "make_place"];
                    let place_pointer = ["ops", "Place", "pointer"];
                    let move_val_init = ["intrinsics", "move_val_init"];
                    let inplace_finalize = ["ops", "InPlace", "finalize"];

                    let unstable_span = self.allow_internal_unstable("<-", e.span);
                    let make_call = |this: &mut LoweringContext, p, args| {
                        let path = P(this.expr_std_path(unstable_span, p, ThinVec::new()));
                        P(this.expr_call(e.span, path, args))
                    };

                    let mk_stmt_let = |this: &mut LoweringContext, bind, expr| {
                        this.stmt_let(e.span, false, bind, expr)
                    };

                    let mk_stmt_let_mut = |this: &mut LoweringContext, bind, expr| {
                        this.stmt_let(e.span, true, bind, expr)
                    };

                    // let placer = <placer_expr> ;
                    let (s1, placer_binding) = {
                        mk_stmt_let(self, placer_ident, placer_expr)
                    };

                    // let mut place = Placer::make_place(placer);
                    let (s2, place_binding) = {
                        let placer = self.expr_ident(e.span, placer_ident, placer_binding);
                        let call = make_call(self, &make_place, hir_vec![placer]);
                        mk_stmt_let_mut(self, place_ident, call)
                    };

                    // let p_ptr = Place::pointer(&mut place);
                    let (s3, p_ptr_binding) = {
                        let agent = P(self.expr_ident(e.span, place_ident, place_binding));
                        let args = hir_vec![self.expr_mut_addr_of(e.span, agent)];
                        let call = make_call(self, &place_pointer, args);
                        mk_stmt_let(self, p_ptr_ident, call)
                    };

                    // pop_unsafe!(EXPR));
                    let pop_unsafe_expr = {
                        self.signal_block_expr(hir_vec![],
                                               value_expr,
                                               e.span,
                                               hir::PopUnsafeBlock(hir::CompilerGenerated),
                                               ThinVec::new())
                    };

                    // push_unsafe!({
                    //     std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR ));
                    //     InPlace::finalize(place)
                    // })
                    let expr = {
                        let ptr = self.expr_ident(e.span, p_ptr_ident, p_ptr_binding);
                        let call_move_val_init =
                            hir::StmtSemi(
                                make_call(self, &move_val_init, hir_vec![ptr, pop_unsafe_expr]),
                                self.next_id());
                        let call_move_val_init = respan(e.span, call_move_val_init);

                        let place = self.expr_ident(e.span, place_ident, place_binding);
                        let call = make_call(self, &inplace_finalize, hir_vec![place]);
                        P(self.signal_block_expr(hir_vec![call_move_val_init],
                                                 call,
                                                 e.span,
                                                 hir::PushUnsafeBlock(hir::CompilerGenerated),
                                                 ThinVec::new()))
                    };

                    let block = self.block_all(e.span, hir_vec![s1, s2, s3], Some(expr));
                    // add the attributes to the outer returned expr node
                    return self.expr_block(P(block), e.attrs.clone());
                }

                ExprKind::Array(ref exprs) => {
                    hir::ExprArray(exprs.iter().map(|x| self.lower_expr(x)).collect())
                }
                ExprKind::Repeat(ref expr, ref count) => {
                    let expr = P(self.lower_expr(expr));
                    let count = self.lower_expr(count);
                    hir::ExprRepeat(expr, self.record_body(count, None))
                }
                ExprKind::Tup(ref elts) => {
                    hir::ExprTup(elts.iter().map(|x| self.lower_expr(x)).collect())
                }
                ExprKind::Call(ref f, ref args) => {
                    let f = P(self.lower_expr(f));
                    hir::ExprCall(f, args.iter().map(|x| self.lower_expr(x)).collect())
                }
                ExprKind::MethodCall(i, ref tps, ref args) => {
                    let tps = tps.iter().map(|x| self.lower_ty(x)).collect();
                    let args = args.iter().map(|x| self.lower_expr(x)).collect();
                    hir::ExprMethodCall(respan(i.span, i.node.name), tps, args)
                }
                ExprKind::Binary(binop, ref lhs, ref rhs) => {
                    let binop = self.lower_binop(binop);
                    let lhs = P(self.lower_expr(lhs));
                    let rhs = P(self.lower_expr(rhs));
                    hir::ExprBinary(binop, lhs, rhs)
                }
                ExprKind::Unary(op, ref ohs) => {
                    let op = self.lower_unop(op);
                    let ohs = P(self.lower_expr(ohs));
                    hir::ExprUnary(op, ohs)
                }
                ExprKind::Lit(ref l) => hir::ExprLit(P((**l).clone())),
                ExprKind::Cast(ref expr, ref ty) => {
                    let expr = P(self.lower_expr(expr));
                    hir::ExprCast(expr, self.lower_ty(ty))
                }
                ExprKind::Type(ref expr, ref ty) => {
                    let expr = P(self.lower_expr(expr));
                    hir::ExprType(expr, self.lower_ty(ty))
                }
                ExprKind::AddrOf(m, ref ohs) => {
                    let m = self.lower_mutability(m);
                    let ohs = P(self.lower_expr(ohs));
                    hir::ExprAddrOf(m, ohs)
                }
                // More complicated than you might expect because the else branch
                // might be `if let`.
                ExprKind::If(ref cond, ref blk, ref else_opt) => {
                    let else_opt = else_opt.as_ref().map(|els| {
                        match els.node {
                            ExprKind::IfLet(..) => {
                                // wrap the if-let expr in a block
                                let span = els.span;
                                let els = P(self.lower_expr(els));
                                let id = self.next_id();
                                let blk = P(hir::Block {
                                    stmts: hir_vec![],
                                    expr: Some(els),
                                    id: id,
                                    rules: hir::DefaultBlock,
                                    span: span,
                                });
                                P(self.expr_block(blk, ThinVec::new()))
                            }
                            _ => P(self.lower_expr(els)),
                        }
                    });

                    hir::ExprIf(P(self.lower_expr(cond)), self.lower_block(blk), else_opt)
                }
                ExprKind::While(ref cond, ref body, opt_ident) => {
                    self.with_loop_scope(e.id, |this|
                        hir::ExprWhile(
                            this.with_loop_condition_scope(|this| P(this.lower_expr(cond))),
                            this.lower_block(body),
                            this.lower_opt_sp_ident(opt_ident)))
                }
                ExprKind::Loop(ref body, opt_ident) => {
                    self.with_loop_scope(e.id, |this|
                        hir::ExprLoop(this.lower_block(body),
                                      this.lower_opt_sp_ident(opt_ident),
                                      hir::LoopSource::Loop))
                }
                ExprKind::Match(ref expr, ref arms) => {
                    hir::ExprMatch(P(self.lower_expr(expr)),
                                   arms.iter().map(|x| self.lower_arm(x)).collect(),
                                   hir::MatchSource::Normal)
                }
                ExprKind::Closure(capture_clause, ref decl, ref body, fn_decl_span) => {
                    self.with_new_loop_scopes(|this| {
                        this.with_parent_def(e.id, |this| {
                            let expr = this.lower_expr(body);
                            hir::ExprClosure(this.lower_capture_clause(capture_clause),
                                             this.lower_fn_decl(decl),
                                             this.record_body(expr, Some(decl)),
                                             fn_decl_span)
                        })
                    })
                }
                ExprKind::Block(ref blk) => hir::ExprBlock(self.lower_block(blk)),
                ExprKind::Assign(ref el, ref er) => {
                    hir::ExprAssign(P(self.lower_expr(el)), P(self.lower_expr(er)))
                }
                ExprKind::AssignOp(op, ref el, ref er) => {
                    hir::ExprAssignOp(self.lower_binop(op),
                                      P(self.lower_expr(el)),
                                      P(self.lower_expr(er)))
                }
                ExprKind::Field(ref el, ident) => {
                    hir::ExprField(P(self.lower_expr(el)), respan(ident.span, ident.node.name))
                }
                ExprKind::TupField(ref el, ident) => {
                    hir::ExprTupField(P(self.lower_expr(el)), ident)
                }
                ExprKind::Index(ref el, ref er) => {
                    hir::ExprIndex(P(self.lower_expr(el)), P(self.lower_expr(er)))
                }
                ExprKind::Range(ref e1, ref e2, lims) => {
                    fn make_struct(this: &mut LoweringContext,
                                   ast_expr: &Expr,
                                   path: &[&str],
                                   fields: &[(&str, &P<Expr>)]) -> hir::Expr {
                        let struct_path = &iter::once(&"ops").chain(path).map(|s| *s)
                                                             .collect::<Vec<_>>();
                        let unstable_span = this.allow_internal_unstable("...", ast_expr.span);

                        if fields.len() == 0 {
                            this.expr_std_path(unstable_span, struct_path,
                                               ast_expr.attrs.clone())
                        } else {
                            let fields = fields.into_iter().map(|&(s, e)| {
                                let expr = P(this.lower_expr(&e));
                                let unstable_span = this.allow_internal_unstable("...", e.span);
                                this.field(Symbol::intern(s), expr, unstable_span)
                            }).collect();
                            let attrs = ast_expr.attrs.clone();

                            this.expr_std_struct(unstable_span, struct_path, fields, None, attrs)
                        }
                    }

                    use syntax::ast::RangeLimits::*;

                    return match (e1, e2, lims) {
                        (&None,         &None,         HalfOpen) =>
                            make_struct(self, e, &["RangeFull"], &[]),

                        (&Some(ref e1), &None,         HalfOpen) =>
                            make_struct(self, e, &["RangeFrom"],
                                                 &[("start", e1)]),

                        (&None,         &Some(ref e2), HalfOpen) =>
                            make_struct(self, e, &["RangeTo"],
                                                 &[("end", e2)]),

                        (&Some(ref e1), &Some(ref e2), HalfOpen) =>
                            make_struct(self, e, &["Range"],
                                                 &[("start", e1), ("end", e2)]),

                        (&None,         &Some(ref e2), Closed)   =>
                            make_struct(self, e, &["RangeToInclusive"],
                                                 &[("end", e2)]),

                        (&Some(ref e1), &Some(ref e2), Closed)   =>
                            make_struct(self, e, &["RangeInclusive", "NonEmpty"],
                                                 &[("start", e1), ("end", e2)]),

                        _ => panic!(self.diagnostic()
                                        .span_fatal(e.span, "inclusive range with no end")),
                    };
                }
                ExprKind::Path(ref qself, ref path) => {
                    hir::ExprPath(self.lower_qpath(e.id, qself, path, ParamMode::Optional))
                }
                ExprKind::Break(opt_ident, ref opt_expr) => {
                    let label_result = if self.is_in_loop_condition && opt_ident.is_none() {
                        hir::Destination {
                            ident: opt_ident,
                            loop_id: Err(hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
                        }
                    } else {
                        self.lower_destination(opt_ident.map(|ident| (e.id, ident)))
                    };
                    hir::ExprBreak(
                            label_result,
                            opt_expr.as_ref().map(|x| P(self.lower_expr(x))))
                }
                ExprKind::Continue(opt_ident) =>
                    hir::ExprAgain(
                        if self.is_in_loop_condition && opt_ident.is_none() {
                            hir::Destination {
                                ident: opt_ident,
                                loop_id: Err(
                                    hir::LoopIdError::UnlabeledCfInWhileCondition).into(),
                            }
                        } else {
                            self.lower_destination(opt_ident.map( |ident| (e.id, ident)))
                        }),
                ExprKind::Ret(ref e) => hir::ExprRet(e.as_ref().map(|x| P(self.lower_expr(x)))),
                ExprKind::InlineAsm(ref asm) => {
                    let hir_asm = hir::InlineAsm {
                        inputs: asm.inputs.iter().map(|&(ref c, _)| c.clone()).collect(),
                        outputs: asm.outputs.iter().map(|out| {
                            hir::InlineAsmOutput {
                                constraint: out.constraint.clone(),
                                is_rw: out.is_rw,
                                is_indirect: out.is_indirect,
                            }
                        }).collect(),
                        asm: asm.asm.clone(),
                        asm_str_style: asm.asm_str_style,
                        clobbers: asm.clobbers.clone().into(),
                        volatile: asm.volatile,
                        alignstack: asm.alignstack,
                        dialect: asm.dialect,
                        expn_id: asm.expn_id,
                    };
                    let outputs =
                        asm.outputs.iter().map(|out| self.lower_expr(&out.expr)).collect();
                    let inputs =
                        asm.inputs.iter().map(|&(_, ref input)| self.lower_expr(input)).collect();
                    hir::ExprInlineAsm(P(hir_asm), outputs, inputs)
                }
                ExprKind::Struct(ref path, ref fields, ref maybe_expr) => {
                    hir::ExprStruct(self.lower_qpath(e.id, &None, path, ParamMode::Optional),
                                    fields.iter().map(|x| self.lower_field(x)).collect(),
                                    maybe_expr.as_ref().map(|x| P(self.lower_expr(x))))
                }
                ExprKind::Paren(ref ex) => {
                    let mut ex = self.lower_expr(ex);
                    // include parens in span, but only if it is a super-span.
                    if e.span.contains(ex.span) {
                        ex.span = e.span;
                    }
                    // merge attributes into the inner expression.
                    let mut attrs = e.attrs.clone();
                    attrs.extend::<Vec<_>>(ex.attrs.into());
                    ex.attrs = attrs;
                    return ex;
                }

                // Desugar ExprIfLet
                // From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
                ExprKind::IfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
                    // to:
                    //
                    //   match <sub_expr> {
                    //     <pat> => <body>,
                    //     [_ if <else_opt_if_cond> => <else_opt_if_body>,]
                    //     _ => [<else_opt> | ()]
                    //   }

                    // `<pat> => <body>`
                    let pat_arm = {
                        let body = self.lower_block(body);
                        let body_expr = P(self.expr_block(body, ThinVec::new()));
                        let pat = self.lower_pat(pat);
                        self.arm(hir_vec![pat], body_expr)
                    };

                    // `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
                    let mut else_opt = else_opt.as_ref().map(|e| P(self.lower_expr(e)));
                    let else_if_arms = {
                        let mut arms = vec![];
                        loop {
                            let else_opt_continue = else_opt.and_then(|els| {
                                els.and_then(|els| {
                                    match els.node {
                                        // else if
                                        hir::ExprIf(cond, then, else_opt) => {
                                            let pat_under = self.pat_wild(e.span);
                                            arms.push(hir::Arm {
                                                attrs: hir_vec![],
                                                pats: hir_vec![pat_under],
                                                guard: Some(cond),
                                                body: P(self.expr_block(then, ThinVec::new())),
                                            });
                                            else_opt.map(|else_opt| (else_opt, true))
                                        }
                                        _ => Some((P(els), false)),
                                    }
                                })
                            });
                            match else_opt_continue {
                                Some((e, true)) => {
                                    else_opt = Some(e);
                                }
                                Some((e, false)) => {
                                    else_opt = Some(e);
                                    break;
                                }
                                None => {
                                    else_opt = None;
                                    break;
                                }
                            }
                        }
                        arms
                    };

                    let contains_else_clause = else_opt.is_some();

                    // `_ => [<else_opt> | ()]`
                    let else_arm = {
                        let pat_under = self.pat_wild(e.span);
                        let else_expr =
                            else_opt.unwrap_or_else(|| self.expr_tuple(e.span, hir_vec![]));
                        self.arm(hir_vec![pat_under], else_expr)
                    };

                    let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
                    arms.push(pat_arm);
                    arms.extend(else_if_arms);
                    arms.push(else_arm);

                    let sub_expr = P(self.lower_expr(sub_expr));
                    // add attributes to the outer returned expr node
                    return self.expr(e.span,
                                     hir::ExprMatch(sub_expr,
                                                    arms.into(),
                                                    hir::MatchSource::IfLetDesugar {
                                                        contains_else_clause: contains_else_clause,
                                                    }),
                                     e.attrs.clone());
                }

                // Desugar ExprWhileLet
                // From: `[opt_ident]: while let <pat> = <sub_expr> <body>`
                ExprKind::WhileLet(ref pat, ref sub_expr, ref body, opt_ident) => {
                    // to:
                    //
                    //   [opt_ident]: loop {
                    //     match <sub_expr> {
                    //       <pat> => <body>,
                    //       _ => break
                    //     }
                    //   }

                    // Note that the block AND the condition are evaluated in the loop scope.
                    // This is done to allow `break` from inside the condition of the loop.
                    let (body, break_expr, sub_expr) = self.with_loop_scope(e.id, |this| (
                        this.lower_block(body),
                        this.expr_break(e.span, ThinVec::new()),
                        this.with_loop_condition_scope(|this| P(this.lower_expr(sub_expr))),
                    ));

                    // `<pat> => <body>`
                    let pat_arm = {
                        let body_expr = P(self.expr_block(body, ThinVec::new()));
                        let pat = self.lower_pat(pat);
                        self.arm(hir_vec![pat], body_expr)
                    };

                    // `_ => break`
                    let break_arm = {
                        let pat_under = self.pat_wild(e.span);
                        self.arm(hir_vec![pat_under], break_expr)
                    };

                    // `match <sub_expr> { ... }`
                    let arms = hir_vec![pat_arm, break_arm];
                    let match_expr = self.expr(e.span,
                                               hir::ExprMatch(sub_expr,
                                                              arms,
                                                              hir::MatchSource::WhileLetDesugar),
                                               ThinVec::new());

                    // `[opt_ident]: loop { ... }`
                    let loop_block = P(self.block_expr(P(match_expr)));
                    let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
                                                  hir::LoopSource::WhileLet);
                    // add attributes to the outer returned expr node
                    let attrs = e.attrs.clone();
                    return hir::Expr { id: e.id, node: loop_expr, span: e.span, attrs: attrs };
                }

                // Desugar ExprForLoop
                // From: `[opt_ident]: for <pat> in <head> <body>`
                ExprKind::ForLoop(ref pat, ref head, ref body, opt_ident) => {
                    // to:
                    //
                    //   {
                    //     let result = match ::std::iter::IntoIterator::into_iter(<head>) {
                    //       mut iter => {
                    //         [opt_ident]: loop {
                    //           match ::std::iter::Iterator::next(&mut iter) {
                    //             ::std::option::Option::Some(<pat>) => <body>,
                    //             ::std::option::Option::None => break
                    //           }
                    //         }
                    //       }
                    //     };
                    //     result
                    //   }

                    // expand <head>
                    let head = self.lower_expr(head);

                    let iter = self.str_to_ident("iter");

                    // `::std::option::Option::Some(<pat>) => <body>`
                    let pat_arm = {
                        let body_block = self.with_loop_scope(e.id, |this| this.lower_block(body));
                        let body_expr = P(self.expr_block(body_block, ThinVec::new()));
                        let pat = self.lower_pat(pat);
                        let some_pat = self.pat_some(e.span, pat);

                        self.arm(hir_vec![some_pat], body_expr)
                    };

                    // `::std::option::Option::None => break`
                    let break_arm = {
                        let break_expr = self.with_loop_scope(e.id, |this|
                            this.expr_break(e.span, ThinVec::new()));
                        let pat = self.pat_none(e.span);
                        self.arm(hir_vec![pat], break_expr)
                    };

                    // `mut iter`
                    let iter_pat = self.pat_ident_binding_mode(e.span, iter,
                                                               hir::BindByValue(hir::MutMutable));

                    // `match ::std::iter::Iterator::next(&mut iter) { ... }`
                    let match_expr = {
                        let iter = P(self.expr_ident(e.span, iter, iter_pat.id));
                        let ref_mut_iter = self.expr_mut_addr_of(e.span, iter);
                        let next_path = &["iter", "Iterator", "next"];
                        let next_path = P(self.expr_std_path(e.span, next_path, ThinVec::new()));
                        let next_expr = P(self.expr_call(e.span, next_path,
                                          hir_vec![ref_mut_iter]));
                        let arms = hir_vec![pat_arm, break_arm];

                        P(self.expr(e.span,
                                    hir::ExprMatch(next_expr, arms,
                                                   hir::MatchSource::ForLoopDesugar),
                                    ThinVec::new()))
                    };

                    // `[opt_ident]: loop { ... }`
                    let loop_block = P(self.block_expr(match_expr));
                    let loop_expr = hir::ExprLoop(loop_block, self.lower_opt_sp_ident(opt_ident),
                                                  hir::LoopSource::ForLoop);
                    let loop_expr = P(hir::Expr {
                        id: e.id,
                        node: loop_expr,
                        span: e.span,
                        attrs: ThinVec::new(),
                    });

                    // `mut iter => { ... }`
                    let iter_arm = self.arm(hir_vec![iter_pat], loop_expr);

                    // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
                    let into_iter_expr = {
                        let into_iter_path = &["iter", "IntoIterator", "into_iter"];
                        let into_iter = P(self.expr_std_path(e.span, into_iter_path,
                                                             ThinVec::new()));
                        P(self.expr_call(e.span, into_iter, hir_vec![head]))
                    };

                    let match_expr = P(self.expr_match(e.span,
                                                       into_iter_expr,
                                                       hir_vec![iter_arm],
                                                       hir::MatchSource::ForLoopDesugar));

                    // `{ let _result = ...; _result }`
                    // underscore prevents an unused_variables lint if the head diverges
                    let result_ident = self.str_to_ident("_result");
                    let (let_stmt, let_stmt_binding) =
                        self.stmt_let(e.span, false, result_ident, match_expr);

                    let result = P(self.expr_ident(e.span, result_ident, let_stmt_binding));
                    let block = P(self.block_all(e.span, hir_vec![let_stmt], Some(result)));
                    // add the attributes to the outer returned expr node
                    return self.expr_block(block, e.attrs.clone());
                }

                // Desugar ExprKind::Try
                // From: `<expr>?`
                ExprKind::Try(ref sub_expr) => {
                    // to:
                    //
                    // match Carrier::translate(<expr>) {
                    //     Ok(val) => #[allow(unreachable_code)] val,
                    //     Err(err) => #[allow(unreachable_code)]
                    //                 return Carrier::from_error(From::from(err)),
                    // }
                    let unstable_span = self.allow_internal_unstable("?", e.span);

                    // Carrier::translate(<expr>)
                    let discr = {
                        // expand <expr>
                        let sub_expr = self.lower_expr(sub_expr);

                        let path = &["ops", "Carrier", "translate"];
                        let path = P(self.expr_std_path(unstable_span, path, ThinVec::new()));
                        P(self.expr_call(e.span, path, hir_vec![sub_expr]))
                    };

                    // #[allow(unreachable_code)]
                    let attr = {
                        // allow(unreachable_code)
                        let allow = {
                            let allow_ident = self.str_to_ident("allow");
                            let uc_ident = self.str_to_ident("unreachable_code");
                            let uc_meta_item = attr::mk_spanned_word_item(e.span, uc_ident);
                            let uc_nested = NestedMetaItemKind::MetaItem(uc_meta_item);
                            let uc_spanned = respan(e.span, uc_nested);
                            attr::mk_spanned_list_item(e.span, allow_ident, vec![uc_spanned])
                        };
                        attr::mk_spanned_attr_outer(e.span, attr::mk_attr_id(), allow)
                    };
                    let attrs = vec![attr];

                    // Ok(val) => #[allow(unreachable_code)] val,
                    let ok_arm = {
                        let val_ident = self.str_to_ident("val");
                        let val_pat = self.pat_ident(e.span, val_ident);
                        let val_expr = P(self.expr_ident_with_attrs(e.span,
                                                                    val_ident,
                                                                    val_pat.id,
                                                                    ThinVec::from(attrs.clone())));
                        let ok_pat = self.pat_ok(e.span, val_pat);

                        self.arm(hir_vec![ok_pat], val_expr)
                    };

                    // Err(err) => #[allow(unreachable_code)]
                    //             return Carrier::from_error(From::from(err)),
                    let err_arm = {
                        let err_ident = self.str_to_ident("err");
                        let err_local = self.pat_ident(e.span, err_ident);
                        let from_expr = {
                            let path = &["convert", "From", "from"];
                            let from = P(self.expr_std_path(e.span, path, ThinVec::new()));
                            let err_expr = self.expr_ident(e.span, err_ident, err_local.id);

                            self.expr_call(e.span, from, hir_vec![err_expr])
                        };
                        let from_err_expr = {
                            let path = &["ops", "Carrier", "from_error"];
                            let from_err = P(self.expr_std_path(unstable_span, path,
                                                                ThinVec::new()));
                            P(self.expr_call(e.span, from_err, hir_vec![from_expr]))
                        };

                        let ret_expr = P(self.expr(e.span,
                                                   hir::Expr_::ExprRet(Some(from_err_expr)),
                                                                       ThinVec::from(attrs)));

                        let err_pat = self.pat_err(e.span, err_local);
                        self.arm(hir_vec![err_pat], ret_expr)
                    };

                    return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm],
                                           hir::MatchSource::TryDesugar);
                }

                ExprKind::Mac(_) => panic!("Shouldn't exist here"),
            },
            span: e.span,
            attrs: e.attrs.clone(),
        }
    }

    fn lower_stmt(&mut self, s: &Stmt) -> SmallVector<hir::Stmt> {
        SmallVector::one(match s.node {
            StmtKind::Local(ref l) => Spanned {
                node: hir::StmtDecl(P(Spanned {
                    node: hir::DeclLocal(self.lower_local(l)),
                    span: s.span,
                }), s.id),
                span: s.span,
            },
            StmtKind::Item(ref it) => {
                // Can only use the ID once.
                let mut id = Some(s.id);
                return self.lower_item_id(it).into_iter().map(|item_id| Spanned {
                    node: hir::StmtDecl(P(Spanned {
                        node: hir::DeclItem(item_id),
                        span: s.span,
                    }), id.take().unwrap_or_else(|| self.next_id())),
                    span: s.span,
                }).collect();
            }
            StmtKind::Expr(ref e) => {
                Spanned {
                    node: hir::StmtExpr(P(self.lower_expr(e)), s.id),
                    span: s.span,
                }
            }
            StmtKind::Semi(ref e) => {
                Spanned {
                    node: hir::StmtSemi(P(self.lower_expr(e)), s.id),
                    span: s.span,
                }
            }
            StmtKind::Mac(..) => panic!("Shouldn't exist here"),
        })
    }

    fn lower_capture_clause(&mut self, c: CaptureBy) -> hir::CaptureClause {
        match c {
            CaptureBy::Value => hir::CaptureByValue,
            CaptureBy::Ref => hir::CaptureByRef,
        }
    }

    fn lower_visibility(&mut self, v: &Visibility) -> hir::Visibility {
        match *v {
            Visibility::Public => hir::Public,
            Visibility::Crate(_) => hir::Visibility::Crate,
            Visibility::Restricted { ref path, id } => {
                hir::Visibility::Restricted {
                    path: P(self.lower_path(id, path, ParamMode::Explicit, true)),
                    id: id
                }
            }
            Visibility::Inherited => hir::Inherited,
        }
    }

    fn lower_defaultness(&mut self, d: Defaultness, has_value: bool) -> hir::Defaultness {
        match d {
            Defaultness::Default => hir::Defaultness::Default { has_value: has_value },
            Defaultness::Final => {
                assert!(has_value);
                hir::Defaultness::Final
            }
        }
    }

    fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
        match *b {
            BlockCheckMode::Default => hir::DefaultBlock,
            BlockCheckMode::Unsafe(u) => hir::UnsafeBlock(self.lower_unsafe_source(u)),
        }
    }

    fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingMode {
        match *b {
            BindingMode::ByRef(m) => hir::BindByRef(self.lower_mutability(m)),
            BindingMode::ByValue(m) => hir::BindByValue(self.lower_mutability(m)),
        }
    }

    fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource {
        match u {
            CompilerGenerated => hir::CompilerGenerated,
            UserProvided => hir::UserProvided,
        }
    }

    fn lower_impl_polarity(&mut self, i: ImplPolarity) -> hir::ImplPolarity {
        match i {
            ImplPolarity::Positive => hir::ImplPolarity::Positive,
            ImplPolarity::Negative => hir::ImplPolarity::Negative,
        }
    }

    fn lower_trait_bound_modifier(&mut self, f: TraitBoundModifier) -> hir::TraitBoundModifier {
        match f {
            TraitBoundModifier::None => hir::TraitBoundModifier::None,
            TraitBoundModifier::Maybe => hir::TraitBoundModifier::Maybe,
        }
    }

    // Helper methods for building HIR.

    fn arm(&mut self, pats: hir::HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
        hir::Arm {
            attrs: hir_vec![],
            pats: pats,
            guard: None,
            body: expr,
        }
    }

    fn field(&mut self, name: Name, expr: P<hir::Expr>, span: Span) -> hir::Field {
        hir::Field {
            name: Spanned {
                node: name,
                span: span,
            },
            span: span,
            expr: expr,
            is_shorthand: false,
        }
    }

    fn expr_break(&mut self, span: Span, attrs: ThinVec<Attribute>) -> P<hir::Expr> {
        let expr_break = hir::ExprBreak(self.lower_destination(None), None);
        P(self.expr(span, expr_break, attrs))
    }

    fn expr_call(&mut self, span: Span, e: P<hir::Expr>, args: hir::HirVec<hir::Expr>)
                 -> hir::Expr {
        self.expr(span, hir::ExprCall(e, args), ThinVec::new())
    }

    fn expr_ident(&mut self, span: Span, id: Name, binding: NodeId) -> hir::Expr {
        self.expr_ident_with_attrs(span, id, binding, ThinVec::new())
    }

    fn expr_ident_with_attrs(&mut self, span: Span,
                                        id: Name,
                                        binding: NodeId,
                                        attrs: ThinVec<Attribute>) -> hir::Expr {
        let def = {
            let defs = self.resolver.definitions();
            Def::Local(defs.local_def_id(binding))
        };

        let expr_path = hir::ExprPath(hir::QPath::Resolved(None, P(hir::Path {
            span: span,
            def: def,
            segments: hir_vec![hir::PathSegment::from_name(id)],
        })));

        self.expr(span, expr_path, attrs)
    }

    fn expr_mut_addr_of(&mut self, span: Span, e: P<hir::Expr>) -> hir::Expr {
        self.expr(span, hir::ExprAddrOf(hir::MutMutable, e), ThinVec::new())
    }

    fn expr_std_path(&mut self,
                     span: Span,
                     components: &[&str],
                     attrs: ThinVec<Attribute>)
                     -> hir::Expr {
        let path = self.std_path(span, components, true);
        self.expr(span, hir::ExprPath(hir::QPath::Resolved(None, P(path))), attrs)
    }

    fn expr_match(&mut self,
                  span: Span,
                  arg: P<hir::Expr>,
                  arms: hir::HirVec<hir::Arm>,
                  source: hir::MatchSource)
                  -> hir::Expr {
        self.expr(span, hir::ExprMatch(arg, arms, source), ThinVec::new())
    }

    fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
        self.expr(b.span, hir::ExprBlock(b), attrs)
    }

    fn expr_tuple(&mut self, sp: Span, exprs: hir::HirVec<hir::Expr>) -> P<hir::Expr> {
        P(self.expr(sp, hir::ExprTup(exprs), ThinVec::new()))
    }

    fn expr_std_struct(&mut self,
                       span: Span,
                       components: &[&str],
                       fields: hir::HirVec<hir::Field>,
                       e: Option<P<hir::Expr>>,
                       attrs: ThinVec<Attribute>) -> hir::Expr {
        let path = self.std_path(span, components, false);
        let qpath = hir::QPath::Resolved(None, P(path));
        self.expr(span, hir::ExprStruct(qpath, fields, e), attrs)
    }

    fn expr(&mut self, span: Span, node: hir::Expr_, attrs: ThinVec<Attribute>) -> hir::Expr {
        hir::Expr {
            id: self.next_id(),
            node: node,
            span: span,
            attrs: attrs,
        }
    }

    fn stmt_let(&mut self, sp: Span, mutbl: bool, ident: Name, ex: P<hir::Expr>)
                -> (hir::Stmt, NodeId) {
        let pat = if mutbl {
            self.pat_ident_binding_mode(sp, ident, hir::BindByValue(hir::MutMutable))
        } else {
            self.pat_ident(sp, ident)
        };
        let pat_id = pat.id;
        let local = P(hir::Local {
            pat: pat,
            ty: None,
            init: Some(ex),
            id: self.next_id(),
            span: sp,
            attrs: ThinVec::new(),
        });
        let decl = respan(sp, hir::DeclLocal(local));
        (respan(sp, hir::StmtDecl(P(decl), self.next_id())), pat_id)
    }

    fn block_expr(&mut self, expr: P<hir::Expr>) -> hir::Block {
        self.block_all(expr.span, hir::HirVec::new(), Some(expr))
    }

    fn block_all(&mut self, span: Span, stmts: hir::HirVec<hir::Stmt>, expr: Option<P<hir::Expr>>)
                 -> hir::Block {
        hir::Block {
            stmts: stmts,
            expr: expr,
            id: self.next_id(),
            rules: hir::DefaultBlock,
            span: span,
        }
    }

    fn pat_ok(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
        self.pat_std_enum(span, &["result", "Result", "Ok"], hir_vec![pat])
    }

    fn pat_err(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
        self.pat_std_enum(span, &["result", "Result", "Err"], hir_vec![pat])
    }

    fn pat_some(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
        self.pat_std_enum(span, &["option", "Option", "Some"], hir_vec![pat])
    }

    fn pat_none(&mut self, span: Span) -> P<hir::Pat> {
        self.pat_std_enum(span, &["option", "Option", "None"], hir_vec![])
    }

    fn pat_std_enum(&mut self,
                    span: Span,
                    components: &[&str],
                    subpats: hir::HirVec<P<hir::Pat>>)
                    -> P<hir::Pat> {
        let path = self.std_path(span, components, true);
        let qpath = hir::QPath::Resolved(None, P(path));
        let pt = if subpats.is_empty() {
            hir::PatKind::Path(qpath)
        } else {
            hir::PatKind::TupleStruct(qpath, subpats, None)
        };
        self.pat(span, pt)
    }

    fn pat_ident(&mut self, span: Span, name: Name) -> P<hir::Pat> {
        self.pat_ident_binding_mode(span, name, hir::BindByValue(hir::MutImmutable))
    }

    fn pat_ident_binding_mode(&mut self, span: Span, name: Name, bm: hir::BindingMode)
                              -> P<hir::Pat> {
        let id = self.next_id();
        let parent_def = self.parent_def;
        let def_id = {
            let defs = self.resolver.definitions();
            let def_path_data = DefPathData::Binding(name.as_str());
            let def_index = defs.create_def_with_parent(parent_def, id, def_path_data);
            DefId::local(def_index)
        };

        P(hir::Pat {
            id: id,
            node: hir::PatKind::Binding(bm,
                                        def_id,
                                        Spanned {
                                            span: span,
                                            node: name,
                                        },
                                        None),
            span: span,
        })
    }

    fn pat_wild(&mut self, span: Span) -> P<hir::Pat> {
        self.pat(span, hir::PatKind::Wild)
    }

    fn pat(&mut self, span: Span, pat: hir::PatKind) -> P<hir::Pat> {
        P(hir::Pat {
            id: self.next_id(),
            node: pat,
            span: span,
        })
    }

    /// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
    /// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
    /// The path is also resolved according to `is_value`.
    fn std_path(&mut self, span: Span, components: &[&str], is_value: bool) -> hir::Path {
        let mut path = hir::Path {
            span: span,
            def: Def::Err,
            segments: iter::once(keywords::CrateRoot.name()).chain({
                self.crate_root.into_iter().chain(components.iter().cloned()).map(Symbol::intern)
            }).map(hir::PathSegment::from_name).collect(),
        };

        self.resolver.resolve_hir_path(&mut path, is_value);
        path
    }

    fn signal_block_expr(&mut self,
                         stmts: hir::HirVec<hir::Stmt>,
                         expr: P<hir::Expr>,
                         span: Span,
                         rule: hir::BlockCheckMode,
                         attrs: ThinVec<Attribute>)
                         -> hir::Expr {
        let id = self.next_id();
        let block = P(hir::Block {
            rules: rule,
            span: span,
            id: id,
            stmts: stmts,
            expr: Some(expr),
        });
        self.expr_block(block, attrs)
    }

    fn ty_path(&mut self, id: NodeId, span: Span, qpath: hir::QPath) -> P<hir::Ty> {
        let mut id = id;
        let node = match qpath {
            hir::QPath::Resolved(None, path) => {
                // Turn trait object paths into `TyTraitObject` instead.
                if let Def::Trait(_) = path.def {
                    let principal = hir::PolyTraitRef {
                        bound_lifetimes: hir_vec![],
                        trait_ref: hir::TraitRef {
                            path: path.and_then(|path| path),
                            ref_id: id,
                        },
                        span,
                    };

                    // The original ID is taken by the `PolyTraitRef`,
                    // so the `Ty` itself needs a different one.
                    id = self.next_id();

                    hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span))
                } else {
                    hir::TyPath(hir::QPath::Resolved(None, path))
                }
            }
            _ => hir::TyPath(qpath)
        };
        P(hir::Ty { id, node, span })
    }

    fn elided_lifetime(&mut self, span: Span) -> hir::Lifetime {
        hir::Lifetime {
            id: self.next_id(),
            span: span,
            name: keywords::Invalid.name()
        }
    }
}

fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body>) -> Vec<hir::BodyId> {
    // Sorting by span ensures that we get things in order within a
    // file, and also puts the files in a sensible order.
    let mut body_ids: Vec<_> = bodies.keys().cloned().collect();
    body_ids.sort_by_key(|b| bodies[b].value.span);
    body_ids
}
